package com.gionee.navil.lockscreen;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;

import java.util.ArrayList;

public class GnKeyguardWidgetPager extends GnPagedView implements
		GnPagedView.PageSwitchListener, OnLongClickListener {
	


	private final static boolean ENABLE_ONLONGPASS_DELETE = true;
	private static float CAMERA_DISTANCE = 10000;
	protected static float OVERSCROLL_MAX_ROTATION = 30;
	private static final boolean PERFORM_OVERSCROLL_ROTATION = false;

	private GnLockScreenUtils mLockScreenUtils;

	// Related to the fading in / out background outlines
	public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
	public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
	protected AnimatorSet mChildrenOutlineFadeAnimation;
	protected int mScreenCenter;
	boolean showHintsAfterLayout = false;

	private static final String TAG = "KeyguardWidgetPager";
	private boolean mCenterSmallWidgetsVertically;

	private int mPage = 0;
	private Callbacks mCallbacks;

	private int mWidgetToResetAfterFadeOut;

	// Background worker thread: used here for persistence, also made available
	// to widget frames
	private final HandlerThread mBackgroundWorkerThread;
	private final Handler mBackgroundWorkerHandler;

	private GnNavilIndicator2 mIndicator = null;

	public GnKeyguardWidgetPager(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public GnKeyguardWidgetPager(Context context) {
		this(null, null, 0);
	}

	public GnKeyguardWidgetPager(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
			setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
		}

		setPageSwitchListener(this);

		mBackgroundWorkerThread = new HandlerThread(
				"KeyguardWidgetPager Worker");
		mBackgroundWorkerThread.start();
		mBackgroundWorkerHandler = new Handler(
				mBackgroundWorkerThread.getLooper());
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();

		// Clean up the worker thread
		mBackgroundWorkerThread.quit();
	}

	public void setLockPatternUtils(GnLockScreenUtils l) {
		mLockScreenUtils = l;
	}

	@Override
	public void onPageSwitching(View newPage, int newPageIndex) {
		GnLog.d(TAG, "onPageSwitching newPageIndex=" + newPageIndex);
	}

	@Override
	public void onPageSwitched(View newPage, int newPageIndex) {
		GnLog.d(TAG, "onPageSwitched newPageIndex=" + newPageIndex);
		// Disable the status bar clock if we're showing the default status
		// widget
		// Extend the display timeout if the user switches pages
		if (mPage != newPageIndex) {
			int oldPageIndex = mPage;
			mPage = newPageIndex;
			userActivity();
			GnKeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
			if (oldWidgetPage != null) {
				oldWidgetPage.onActive(false);
			}
			GnKeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
			if (newWidgetPage != null) {
				newWidgetPage.onActive(true);
				newWidgetPage.requestAccessibilityFocus();
			}
			if (mParent != null
					&& AccessibilityManager.getInstance(mContext).isEnabled()) {
				AccessibilityEvent event = AccessibilityEvent
						.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
				onInitializeAccessibilityEvent(event);
				onPopulateAccessibilityEvent(event);
				mParent.requestSendAccessibilityEvent(this, event);
			}

		}
		
		if (mIndicator != null) {
			mIndicator.onPageSwitch(newPageIndex);
		}

	}

	@Override
	public void sendAccessibilityEvent(int eventType) {
		if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED
				|| isPageMoving()) {
			super.sendAccessibilityEvent(eventType);
		}
	}

	private void updateWidgetFramesImportantForAccessibility() {
		final int pageCount = getPageCount();
		for (int i = 0; i < pageCount; i++) {
			GnKeyguardWidgetFrame frame = getWidgetPageAt(i);
			updateWidgetFrameImportantForAccessibility(frame);
		}
	}

	private void updateWidgetFrameImportantForAccessibility(
			GnKeyguardWidgetFrame frame) {
		if (frame.getContentAlpha() <= 0) {
			frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
		} else {
			frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
		}
	}

	private void userActivity() {
		if (mCallbacks != null) {
			mCallbacks.onUserActivityTimeoutChanged();
			mCallbacks.userActivity();
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// GnLog.d(TAG, "onTouchEvent");
		return captureUserInteraction(ev) || super.onTouchEvent(ev);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// GnLog.d(TAG, "onInterceptTouchEvent");
		return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev);
	}

	private boolean captureUserInteraction(MotionEvent ev) {
		GnKeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
		return currentWidgetPage != null
				&& currentWidgetPage.onUserInteraction(ev);
	}

	public void showPagingFeedback() {
		// Nothing yet.
	}

	public long getUserActivityTimeout() {
		return -1;
	}

	public void setCallbacks(Callbacks callbacks) {
		mCallbacks = callbacks;
	}

	public interface Callbacks {
		public void userActivity();

		public void onUserActivityTimeoutChanged();

		public void onAddView(View v);

		public void onRemoveView(View v, boolean draging);
	}

	public void addWidget(View widget) {
		addWidget(widget, -1);
	}

	public void onRemoveView(View v, boolean draging) {
		final int appWidgetId = ((GnKeyguardWidgetFrame) v)
				.getContentAppWidgetId();
		if (mCallbacks != null) {
			mCallbacks.onRemoveView(v, draging);
		}
		mBackgroundWorkerHandler.post(new Runnable() {
			@Override
			public void run() {
				mLockScreenUtils.removeAppWidget(appWidgetId);
			}
		});
	}

	public void onAddView(View v, final int index) {
		final int appWidgetId = ((GnKeyguardWidgetFrame) v)
				.getContentAppWidgetId();
		final int[] pagesRange = new int[mTempVisiblePagesRange.length];
		getVisiblePages(pagesRange);
		boundByReorderablePages(true, pagesRange);
		if (mCallbacks != null) {
			mCallbacks.onAddView(v);
		}
		// Subtract from the index to take into account pages before the
		// reorderable
		// pages (e.g. the "add widget" page)
		mBackgroundWorkerHandler.post(new Runnable() {
			@Override
			public void run() {
				mLockScreenUtils.addAppWidget(appWidgetId, index
						- pagesRange[0]);
			}
		});
	}

	/*
	 * We wrap widgets in a special frame which handles drawing the over scroll
	 * foreground.
	 */
	public void addWidget(View widget, int pageIndex) {
		GnKeyguardWidgetFrame frame;
		// All views contained herein should be wrapped in a KeyguardWidgetFrame
		if (!(widget instanceof GnKeyguardWidgetFrame)) {
			frame = new GnKeyguardWidgetFrame(getContext());
			FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
					LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
			lp.gravity = Gravity.TOP;

			// The framework adds a default padding to AppWidgetHostView. We
			// don't need this padding
			// for the Keyguard, so we override it to be 0.
			widget.setPadding(0, 0, 0, 0);
			frame.addView(widget, lp);

			// We set whether or not this widget supports vertical resizing.
			if (widget instanceof AppWidgetHostView) {
				AppWidgetHostView awhv = (AppWidgetHostView) widget;
				AppWidgetProviderInfo info = awhv.getAppWidgetInfo();
				if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
					frame.setWidgetLockedSmall(false);
				} else {
					// Lock the widget to be small.
					frame.setWidgetLockedSmall(true);
					if (mCenterSmallWidgetsVertically) {
						lp.gravity = Gravity.CENTER;
					}
				}
			}
		} else {
			frame = (GnKeyguardWidgetFrame) widget;
		}

		ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
				ViewGroup.LayoutParams.MATCH_PARENT,
				ViewGroup.LayoutParams.MATCH_PARENT);
		if (ENABLE_ONLONGPASS_DELETE) {
			frame.setOnLongClickListener(this);
		}
		frame.setWorkerHandler(mBackgroundWorkerHandler);

		if (pageIndex == -1) {
			addView(frame, pageLp);
		} else {
			addView(frame, pageIndex, pageLp);
		}

		// Update the frame content description.
		View content = (widget == frame) ? frame.getContent() : widget;
		if (content != null) {
			String contentDescription = mContext
					.getString(
							com.android.internal.R.string.keyguard_accessibility_widget,
							content.getContentDescription());
			frame.setContentDescription(contentDescription);
		}
		updateWidgetFrameImportantForAccessibility(frame);
	}

	/**
	 * Use addWidget() instead.
	 * 
	 * @deprecated
	 */
	@Override
	public void addView(View child, int index) {
		enforceKeyguardWidgetFrame(child);
		super.addView(child, index);
	}

	/**
	 * Use addWidget() instead.
	 * 
	 * @deprecated
	 */
	@Override
	public void addView(View child, int width, int height) {
		enforceKeyguardWidgetFrame(child);
		super.addView(child, width, height);
	}

	/**
	 * Use addWidget() instead.
	 * 
	 * @deprecated
	 */
	@Override
	public void addView(View child, LayoutParams params) {
		enforceKeyguardWidgetFrame(child);
		super.addView(child, params);
	}

	/**
	 * Use addWidget() instead.
	 * 
	 * @deprecated
	 */
	@Override
	public void addView(View child, int index, LayoutParams params) {
		enforceKeyguardWidgetFrame(child);
		super.addView(child, index, params);
	}

	private void enforceKeyguardWidgetFrame(View child) {
		if (!(child instanceof GnKeyguardWidgetFrame)) {
			throw new IllegalArgumentException(
					"KeyguardWidgetPager children must be KeyguardWidgetFrames");
		}
	}

	public GnKeyguardWidgetFrame getWidgetPageAt(int index) {
		// This is always a valid cast as we've guarded the ability to
		return (GnKeyguardWidgetFrame) getChildAt(index);
	}

	protected void onUnhandledTap(MotionEvent ev) {
		showPagingFeedback();
	}

	@Override
	protected void onPageBeginMoving() {
		GnLog.d(TAG, "onPageBeginMoving");
		if (!isReordering(false)) {
			showOutlinesAndSidePages();
		}
		userActivity();
	}

	@Override
	protected void onPageEndMoving() {
		GnLog.d(TAG, "onPageEndMoving");
		if (!isReordering(false)) {
			hideOutlinesAndSidePages();
		}
	}

	protected void enablePageContentLayers() {
		int children = getChildCount();
		for (int i = 0; i < children; i++) {
			getWidgetPageAt(i).enableHardwareLayersForContent();
		}
	}

	protected void disablePageContentLayers() {
		int children = getChildCount();
		for (int i = 0; i < children; i++) {
			getWidgetPageAt(i).disableHardwareLayersForContent();
		}
	}

	@Override
	protected void overScroll(float amount) {
		GnLog.d(TAG, "overScroll");
		acceleratedOverScroll(amount);
	}

	private void updatePageAlphaValues(int screenCenter) {
	}

	public float getAlphaForPage(int screenCenter, int index) {
		return 1f;
	}

	public float getOutlineAlphaForPage(int screenCenter, int index) {
		return getAlphaForPage(screenCenter, index)
				* GnKeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
	}

	protected boolean isOverScrollChild(int index, float scrollProgress) {
		boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
		return (isInOverscroll && (index == 0 && scrollProgress < 0 || index == getChildCount() - 1
				&& scrollProgress > 0));
	}

	@Override
	protected void screenScrolled(int screenCenter) {
		GnLog.d(TAG, "screenScrolled screenCenter =" + screenCenter);
		mScreenCenter = screenCenter;
		updatePageAlphaValues(screenCenter);
		updatePageIndicator(screenCenter);
		for (int i = 0; i < getChildCount(); i++) {
			GnKeyguardWidgetFrame v = getWidgetPageAt(i);
			if (v == mDragView)
				continue;
			if (v != null) {
				float scrollProgress = getScrollProgress(screenCenter, v, i);

				v.setCameraDistance(mDensity * CAMERA_DISTANCE);

				if (isOverScrollChild(i, scrollProgress)
						&& PERFORM_OVERSCROLL_ROTATION) {
					float pivotX = v.getMeasuredWidth() / 2;
					float pivotY = v.getMeasuredHeight() / 2;
					v.setPivotX(pivotX);
					v.setPivotY(pivotY);
					v.setRotationY(-OVERSCROLL_MAX_ROTATION * scrollProgress);
					v.setOverScrollAmount(Math.abs(scrollProgress),
							scrollProgress < 0);
				} else {
					v.setRotationY(0f);
					v.setOverScrollAmount(0, false);
				}

				float alpha = v.getAlpha();
				// If the view has 0 alpha, we set it to be invisible so as to
				// prevent
				// it from accepting touches
				if (alpha == 0) {
					GnLog.d(TAG, "made 1");
					v.setVisibility(INVISIBLE);
				} else if (v.getVisibility() != VISIBLE) {
					GnLog.d(TAG, "made 2");
					v.setVisibility(VISIBLE);
				}
			}
		}
	}

	public boolean isWidgetPage(int pageIndex) {
		if (pageIndex < 0 || pageIndex >= getChildCount()) {
			return false;
		}
		View v = getChildAt(pageIndex);
		if (v != null && v instanceof GnKeyguardWidgetFrame) {
			GnKeyguardWidgetFrame kwf = (GnKeyguardWidgetFrame) v;
			return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
		}
		return false;
	}

	/**
	 * Returns the bounded set of pages that are re-orderable. The range is
	 * fully inclusive.
	 */
	@Override
	void boundByReorderablePages(boolean isReordering, int[] range) {
		GnLog.d(TAG, "boundByReorderablePages");
		if (isReordering) {
			// Remove non-widget pages from the range
			while (range[1] >= range[0] && !isWidgetPage(range[1])) {
				GnLog.d(TAG, "boundByReorderablePages 1");
				range[1]--;
			}
			while (range[0] <= range[1] && !isWidgetPage(range[0])) {
				GnLog.d(TAG, "boundByReorderablePages 2");
				range[0]++;
			}
		}
		GnLog.d(TAG, "boundByReorderablePages range[0]=" + range[0]
				+ ",range[1]=" + range[1]);
	}

	protected void reorderStarting() {
		showOutlinesAndSidePages();
	}

	@Override
	protected void onStartReordering() {
		GnLog.d(TAG, "onStartReordering");
		super.onStartReordering();
		enablePageContentLayers();
		reorderStarting();
	}

	@Override
	protected void onEndReordering() {
		GnLog.d(TAG, "onEndReordering");
		super.onEndReordering();
		hideOutlinesAndSidePages();
		resetIndicator();
	}

	void showOutlinesAndSidePages() {
		GnLog.d(TAG, "showOutlinesAndSidePages");
		animateOutlinesAndSidePages(true);
	}

	void hideOutlinesAndSidePages() {
		GnLog.d(TAG, "hideOutlinesAndSidePages");
		animateOutlinesAndSidePages(false);
	}

	public void showInitialPageHints() {
		GnLog.d(TAG, "showInitialPageHints");
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			GnKeyguardWidgetFrame child = getWidgetPageAt(i);
			if (i != mCurrentPage) {
				child.fadeFrame(this, true,
						GnKeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER,
						CHILDREN_OUTLINE_FADE_IN_DURATION);
				child.setContentAlpha(0f);
			} else {
				child.setBackgroundAlpha(0f);
				child.setContentAlpha(1f);
			}
		}
	}

	@Override
	void setCurrentPage(int currentPage) {
		super.setCurrentPage(currentPage);
		updateWidgetFramesImportantForAccessibility();
	}

	@Override
	public void onAttachedToWindow() {
		super.onAttachedToWindow();
	}

	void animateOutlinesAndSidePages(final boolean show) {
		animateOutlinesAndSidePages(show, -1);
	}

	public void setWidgetToResetOnPageFadeOut(int widget) {
		mWidgetToResetAfterFadeOut = widget;
	}

	public int getWidgetToResetOnPageFadeOut() {
		return mWidgetToResetAfterFadeOut;
	}

	void animateOutlinesAndSidePages(final boolean show, int duration) {
		if (mChildrenOutlineFadeAnimation != null) {
			mChildrenOutlineFadeAnimation.cancel();
			mChildrenOutlineFadeAnimation = null;
		}
		int count = getChildCount();
		PropertyValuesHolder alpha;
		ArrayList<Animator> anims = new ArrayList<Animator>();

		if (duration == -1) {
			duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION
					: CHILDREN_OUTLINE_FADE_OUT_DURATION;
		}

		int curPage = getNextPage();
		for (int i = 0; i < count; i++) {
			float finalContentAlpha;
			if (show) {
				finalContentAlpha = getAlphaForPage(mScreenCenter, i);
			} else if (!show && i == curPage) {
				finalContentAlpha = 1f;
			} else {
				finalContentAlpha = 0f;
			}
			GnKeyguardWidgetFrame child = getWidgetPageAt(i);

			alpha = PropertyValuesHolder.ofFloat("contentAlpha",
					finalContentAlpha);
			ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child,
					alpha);
			anims.add(a);

			float finalOutlineAlpha = show ? getOutlineAlphaForPage(
					mScreenCenter, i) : 0f;
			child.fadeFrame(this, show, finalOutlineAlpha, duration);
		}

		mChildrenOutlineFadeAnimation = new AnimatorSet();
		mChildrenOutlineFadeAnimation.playTogether(anims);

		mChildrenOutlineFadeAnimation.setDuration(duration);
		mChildrenOutlineFadeAnimation
				.addListener(new AnimatorListenerAdapter() {
					@Override
					public void onAnimationStart(Animator animation) {
						if (show) {
							enablePageContentLayers();
						}
					}

					@Override
					public void onAnimationEnd(Animator animation) {
						if (!show) {
							disablePageContentLayers();
							GnKeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut);
							if (frame != null
									&& !(frame == getWidgetPageAt(mCurrentPage))) {
								frame.resetSize();
							}
							mWidgetToResetAfterFadeOut = -1;
						}
						updateWidgetFramesImportantForAccessibility();
					}
				});
		mChildrenOutlineFadeAnimation.start();
	}

	@Override
	public boolean onLongClick(View v) {

		GnLog.d(TAG, "onLongClick");
		if (startReordering()) {
			return true;
		}
		return false;
	}

	public void removeWidget(View view) {
		if (view instanceof GnKeyguardWidgetFrame) {
			removeView(view);
		} else {
			// Assume view was wrapped by a KeyguardWidgetFrame in
			// KeyguardWidgetPager#addWidget().
			// This supports legacy hard-coded "widgets" like
			// KeyguardTransportControlView.
			int pos = getWidgetPageIndex(view);
			if (pos != -1) {
				GnKeyguardWidgetFrame frame = (GnKeyguardWidgetFrame) getChildAt(pos);
				frame.removeView(view);
				removeView(frame);
			} else {
				Slog.w(TAG, "removeWidget() can't find:" + view);
			}
		}
	}

	public int getWidgetPageIndex(View view) {
		if (view instanceof GnKeyguardWidgetFrame) {
			return indexOfChild(view);
		} else {
			// View was wrapped by a KeyguardWidgetFrame by
			// KeyguardWidgetPager#addWidget()
			return indexOfChild((GnKeyguardWidgetFrame) view.getParent());
		}
	}

	@Override
	protected void setPageHoveringOverDeleteDropTarget(int viewIndex,
			boolean isHovering) {
		GnKeyguardWidgetFrame child = getWidgetPageAt(viewIndex);
		child.setIsHoveringOverDeleteDropTarget(isHovering);
	}

	public void setIndicator(GnNavilIndicator2 indicator) {
		mIndicator = indicator;
		resetIndicator();
	}

	public void resetIndicator() {
		if (mIndicator != null) {
			mIndicator.setCountPage(getChildCount());
			mIndicator.setCurPage(getCurrentPage());
		}
	}

	private void updatePageIndicator(int screenCenter) {
		if (mIndicator != null) {
			mIndicator.move(screenCenter);
		}
	}

}
